/**
 * 
 */
package org.going.annotation;

import java.io.DataInput;
import java.io.IOException;
import java.util.Set;

/**
 * @author ferendo.huang@gmail.com
 * 
 */
public class ClassByteCodeFilter
{
	// Constants used to define type in cp_info structure
	private static final int CP_INFO_CLASS = 7;
	private static final int CP_INFO_FIELD_REF = 9;
	private static final int CP_INFO_METHOD_REF = 10;
	private static final int CP_INFO_INTERFACE_REF = 11;
	private static final int CP_INFO_STRING = 8;
	private static final int CP_INFO_INTEGER = 3;
	private static final int CP_INFO_FLOAT = 4;
	private static final int CP_INFO_LONG = 5;
	private static final int CP_INFO_DOUBLE = 6;
	private static final int CP_INFO_NAME_AND_TYPE = 12;
	private static final int CP_INFO_UTF8 = 1;

	/**
	 * Checks if the .class file referenced by the DataInput could contain the
	 * annotation names available in the set.
	 * 
	 * @param in
	 * @param byteCodeAnnotationsNames
	 * @return
	 * @throws IOException
	 */
	public boolean containAnnotationsOnClassDef(DataInput in, Set<String> byteCodeAnnotationsNames)
			throws IOException
	{

		int magic = in.readInt(); // u4

		if (magic != 0xCAFEBABE)
		{
			// the file is not recognized as a class file
			return false;
		}
		// u2 but since in java does not exists unsigned,
		// store on a bigger value
		@SuppressWarnings("unused")
		int minorVersion = in.readUnsignedShort();// u2
		int majorVersion = in.readUnsignedShort();// u2

		if (majorVersion < 49)
		{
			// Compiled with jdk 1.4, so does not have annotations
			return false;
		}

		// constantsPoolCount is the number of entries + 1
		// The index goes from 1 to constantsPoolCount-1
		int constantsPoolCount = in.readUnsignedShort();

		for (int i = 1; i < constantsPoolCount; i++)
		{
			// Format:
			// cp_info {
			// u1 tag;
			// u1 info[];
			// }
			int tag = in.readUnsignedByte();

			switch (tag)
			{
			case CP_INFO_UTF8:
				// u2 length
				// u1 bytes[length]
				// Check if the string is a annotation reference
				// name
				String name = in.readUTF();
				if (byteCodeAnnotationsNames.contains(name))
				{
					return true;
				}
				break;
			case CP_INFO_CLASS: // ignore
				// u2 name_index
				in.readUnsignedShort();
				break;
			case CP_INFO_FIELD_REF: // ignore
			case CP_INFO_METHOD_REF: // ignore
			case CP_INFO_INTERFACE_REF: // ignore
				// u2 class_index
				// u2 name_and_type_index
				in.readUnsignedShort();
				in.readUnsignedShort();
				break;
			case CP_INFO_STRING: // ignore
				// u2 string_index
				in.readUnsignedShort();
				break;
			case CP_INFO_INTEGER: // ignore
			case CP_INFO_FLOAT: // ignore
				// u4 bytes
				in.readInt();
				break;
			case CP_INFO_LONG: // ignore
			case CP_INFO_DOUBLE: // ignore
				// u4 high_bytes
				// u4 low_bytes
				in.readInt();
				in.readInt();
				break;
			case CP_INFO_NAME_AND_TYPE: // ignore
				// u2 name_index
				// u2 descriptor_index
				in.readUnsignedShort();
				in.readUnsignedShort();
				break;
			default:
				// TODO: THIS SHOULD NOT HAPPEN! Log error info
				// and break for loop, because from this point
				// we are reading corrupt data.
				i = constantsPoolCount;
				break;
			}
		}
		return false;
	}
}
